// Copyright (c) 2009 Alistair Jones
// This program is made available under the terms of the MIT License.
package org.hypirinha.html.codegen;

import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import java.util.Set;
import java.util.HashSet;
import java.util.List;
import java.util.ArrayList;

public class AttributeFinder {
    private Document schema;

    public AttributeFinder(Document schema) {
        this.schema = schema;
    }

    public Set<String> findAttributes(String elementName) {
        XPath xPath = XPathUtil.createXPath();

        try {
            Node elementNode = (Node) xPath.evaluate("/xs:schema/xs:element[@name = '" + elementName + "']",
                    schema, XPathConstants.NODE);

            Set<String> groupsAlreadyVisited = new HashSet<String>();
            return findAttributes(xPath, elementNode, groupsAlreadyVisited);

        } catch (XPathExpressionException e) {
            throw new RuntimeException(e);
        }
    }

    private Set<String> findAttributes(XPath xPath, Node elementNode, Set<String> groupsAlreadyVisited) throws XPathExpressionException {
        Set<String> subList = new HashSet<String>();

        NodeList referencedGroups = (NodeList) xPath.evaluate(".//xs:attributeGroup/@ref", elementNode, XPathConstants.NODESET);

        for (int i = 0; i < referencedGroups.getLength(); i++) {
            String groupName = referencedGroups.item(i).getNodeValue();
            Node groupNode = (Node) xPath.evaluate("/xs:schema/xs:attributeGroup[@name = '"
                    + groupName + "']", schema, XPathConstants.NODE);
            if (!groupsAlreadyVisited.contains(groupName)) {
                groupsAlreadyVisited.add(groupName);
                subList.addAll(findAttributes(xPath, groupNode, groupsAlreadyVisited));
            }
        }
//
//        NodeList referencedComplexTypes = (NodeList) xPath.evaluate(".//xs:extension/@base", elementNode, XPathConstants.NODESET);
//
//        for (int i = 0; i < referencedComplexTypes.getLength(); i++) {
//            String complexTypeName = referencedComplexTypes.item(i).getNodeValue();
//            Node complexTypeNode = (Node) xPath.evaluate("/xs:schema/xs:complexType[@name = '"
//                    + complexTypeName + "']", schema, XPathConstants.NODE);
//            subList.addAll(findChildren(xPath, complexTypeNode, groupsAlreadyVisited));
//        }

        subList.addAll(listAttributes(xPath, elementNode, ".//xs:attribute/@name"));
        subList.addAll(listAttributes(xPath, elementNode, ".//xs:attribute/@ref"));
        return subList;
    }

    private List<String> listAttributes(XPath xPath, Node elementNode, String expression) throws XPathExpressionException {
        List<String> attributes = new ArrayList<String>();
        NodeList referencedElements = (NodeList) xPath.evaluate(expression, elementNode, XPathConstants.NODESET);

        for (int i = 0; i < referencedElements.getLength(); i++) {
            attributes.add(referencedElements.item(i).getNodeValue());
        }
        return attributes;
    }

}
